package top.v5it.japi.plus.core.spring.web;

import cn.hutool.core.collection.CollUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import lombok.extern.slf4j.Slf4j;
import top.v5it.japi.plus.common.func.Func1;
import top.v5it.japi.plus.common.func.VoidFunc0;
import top.v5it.japi.plus.common.util.JackSonUtil;
import top.v5it.japi.plus.core.ConstantPool;
import top.v5it.japi.plus.common.tuple.Tuple3;

import java.util.List;
import java.util.Optional;

import static top.v5it.japi.plus.core.ConstantPool.RESOURCE;
import static top.v5it.japi.plus.core.ConstantPool.RESOURCE_TYPE;

/**
 * @author zhanpu
 * @date 2021/3/14
 */
@Slf4j
public class JapiEadaUtil {

    private JapiEadaUtil() {
        throw new UnsupportedOperationException();
    }

    /**
     * 查找并替换资源
     *
     * @param jsonNode {@link JsonNode}
     * @param mapper   true加密，false解密
     * @return 替换后资源
     */
    public static JsonNode findResourceApply(JsonNode jsonNode, Tuple3<Boolean, JapiEada, Func1<String, String, RuntimeException>> mapper) {

        List<JsonNode> parents;

        // 解密，按请求参数中的resource_type处理
        if (!mapper.getOne()) {
            // 一级查找resource_type
            if (jsonNode.has(RESOURCE_TYPE)) {

                resourceReplace(jsonNode)
                        .apply(mapper)
                        .accept();
                return jsonNode;
            }

            // 深度查找resource_type，并获取节点数据
            parents = jsonNode.findParents(RESOURCE_TYPE);

            if (CollUtil.isNotEmpty(parents)) {

                for (JsonNode node : parents) {
                    resourceReplace(node)
                            .apply(mapper)
                            .accept();
                }

                return jsonNode;
            }
        }

        // 一级查找resource
        if (jsonNode.has(RESOURCE)) {
            resourceReplace(jsonNode)
                    .apply(mapper)
                    .accept();
            return jsonNode;
        }

        // 深度查找resource，并获取节点数据
        parents = jsonNode.findParents(RESOURCE);

        if (CollUtil.isNotEmpty(parents)) {

            for (JsonNode node : parents) {
                resourceReplace(node)
                        .apply(mapper)
                        .accept();
            }
        }

        return jsonNode;
    }

    /**
     * 资源替换
     *
     * @param jsonNode {@link JsonNode}资源
     * @return {@link Func1<Boolean, VoidFunc0<RuntimeException>, RuntimeException>}
     */
    private static Func1<Tuple3<Boolean, JapiEada, Func1<String, String, RuntimeException>>, VoidFunc0<RuntimeException>, RuntimeException> resourceReplace(JsonNode jsonNode) {
        return (mapper) -> () -> {
            ObjectNode objectNode = (ObjectNode) jsonNode;

            // 加密时才设置resourceType
            Optional.of(mapper.getTwo())
                    .map(JapiEada::type)
                    .filter(v -> mapper.getOne())
                    .ifPresent(v -> objectNode.set(RESOURCE_TYPE, new TextNode(v.name())));

            // 解密按请求参数中resource_type类型处理，否则按服务器要求的resource_type处理
            JapiEada.Type type = Optional.of(mapper.getOne())
                    .filter(v -> !v)
                    .flatMap(v ->
                            Optional.ofNullable(jsonNode.get(RESOURCE_TYPE))
                                    .map(JsonNode::toString)
                                    .map(JapiEada.Type::match))
                    .orElse(mapper.getTwo().type());

            // resource存在处理resource加解密
            Optional.ofNullable(jsonNode.get(RESOURCE))
                    .map(JsonNode::toString)
                    .flatMap(resource -> resourceApply(type, mapper.getThree()).apply(resource))
                    .ifPresent(resource -> {
                        JsonNode node;

                        if (mapper.getOne()) {
                            node = new TextNode(resource);
                        } else {
                            try {
                                node = JackSonUtil.getMapper().readTree(resource);
                            } catch (JsonProcessingException e) {
                                log.error(e.getMessage(), e);
                                throw new IllegalArgumentException("请求参数[resource]解密后格式错误");
                            }
                        }

                        objectNode.replace(ConstantPool.RESOURCE, node);
                    });
        };
    }

    /**
     * 资源应用
     *
     * @param type   资源类型{@link JapiEada.Type}
     * @param mapper 是否采用加解密，true加密，false解密
     * @return 加解密函数
     */
    public static Func1<String, Optional<String>, RuntimeException> resourceApply(JapiEada.Type type, Func1<String, String, RuntimeException> mapper) {
        return (resource) ->
                Optional.of(type)
                        .filter(JapiEada.Type.ENCRYPT::equals)
                        .map(v -> mapper.apply(resource));
    }
}
